package uts.sdk.modules.kuxDeepmerge
import io.dcloud.uts.*
import io.dcloud.uts.UTSAndroid

private fun defaultIsMergeableObject(value: Any): Boolean {
	return value != null && UTSAndroid.`typeof`(value) === "object" && !UTSArray.isArray(value)
}

fun <T : Any> merge(target: T, source: Any, options: MergeOptions = MergeOptions()): T {
    return when {
        target is UTSJSONObject && source is UTSJSONObject -> {
            mergeUTSJSONObject(target, source, options) as T
        }
        target is UTSArray<*> && source is UTSArray<*> -> {
            val targetArr = target as UTSArray<Any>
            val sourceArr = source as UTSArray<Any>
            val result = options.arrayMerge?.invoke(targetArr, sourceArr, options)
                ?: defaultArrayMerge(targetArr, sourceArr, options)
            result as T
        }
        else -> cloneValue(source, options) as T
    }
}

private fun mergeUTSJSONObject(
    target: UTSJSONObject,
    source: UTSJSONObject,
    options: MergeOptions
): UTSJSONObject {
    val merged = UTSJSONObject()
    UTSJSONObject.keys(target).forEach { key ->
        merged[key] = cloneValue(target[key]!!, options)
    }
    UTSJSONObject.keys(source).forEach { key ->
        val sourceVal = source[key] ?: return@forEach
        if (merged.toMap().has(key)) {
            val targetVal = merged[key]
			val mergedVal = options.customMerge?.invoke(key)?.invoke(targetVal!!, sourceVal, options)
				?: when {
					targetVal != null && isMergeable(targetVal, options) && isMergeable(sourceVal, options) ->
						merge(targetVal, sourceVal, options)

					targetVal is UTSArray<*> && sourceVal is UTSArray<*> ->
						merge(targetVal, sourceVal, options)

					else -> cloneValue(sourceVal, options)
				}

            merged[key] = mergedVal
        } else {
            merged[key] = cloneValue(sourceVal, options)
        }
    }
    return merged
}

fun <T : Any> mergeAll(objects: UTSArray<Any>, options: MergeOptions = MergeOptions()): T {
	if (objects.length == 0) throw IllegalArgumentException("objects is empty")
	return objects.reduce<Any, Any> { acc, obj -> merge(acc, obj, options) } as T
}

@Suppress("UNCHECKED_CAST")
private fun defaultArrayMerge(
	target: UTSArray<Any>, 
	source: UTSArray<Any>, 
	options: MergeOptions
): UTSArray<Any> {
	return UTSArray<Any>().apply {
		target.forEach { 
			cloneValue(it, options)?.let { push(it) }
		}
		source.forEach { 
			cloneValue(it, options)?.let { push(it) }
		}
	}
}

private fun cloneValue(value: Any, options: MergeOptions): Any? {
	// 优先调用用户自定义的克隆决策函数
	options.cloneUnlessOtherwiseSpecified?.invoke(value, options)?.let { return it }
	// 当 clone 为 null 时默认启用克隆
	val shouldClone = options.clone ?: true
	if (!shouldClone) return value

	return when (value) {
		is UTSJSONObject -> {
			UTSJSONObject().apply {
				UTSJSONObject.keys(value).forEach { key ->
					this[key] = cloneValue(value[key]!!, options)
				}
			}
		}
		is UTSArray<*> -> {
			UTSArray<Any?>().apply {
				value.forEach { item -> push(cloneValue(item!!, options)) }
			}
		}
		is Map<*, *> -> {
			value.entries.associate { (k, v) ->
				k to cloneValue(v!!, options)
			}.toMutableMap()
		}
		else -> value
	}
}

private fun isMergeable(value: Any, options: MergeOptions): Boolean {
    return options.isMergeableObject?.invoke(value) ?: defaultIsMergeableObject(value)
}